#ifndef _COMMANDS_H_
#define _COMMANDS_H_

#include <string>
#include <deque>
#include <iostream>
#include <ostream>
#include <algorithm>
#include "IVariableDict.h"
#include "InfoAnalizer.h"
#include "Functions.h"

class AbstractCommand {
protected:
  std::string startCommand;

  AbstractCommand(std::string startCommand_) {
    startCommand = startCommand_;
  }
public:
  virtual ~AbstractCommand() {}
  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) = 0;
  virtual int getTime() = 0;
};

class AbstractParser {
public:
  virtual AbstractCommand *getCommand(const std::string &commandStr) = 0;
};

class AssignCommand : public AbstractCommand {
private:
  std::string variableName;
  int variableValue;
public: 
  AssignCommand(std::string startCommand_) : AbstractCommand(startCommand_) {
    int index = startCommand.find("=");
    variableName = startCommand.substr(0, index - 1);
    variableName = getTrimLine(variableName);
    std::string valueStr = getTrimLine(startCommand.substr(index + 1));
    variableValue = atoi(valueStr.c_str());          
  }

  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) {
    variables->setValue(variableName, variableValue);   
    return true;
  }

  virtual int getTime() {
    return InfoAnalizer::getAssigmentTime();
  } 
};

class AssignParser : public AbstractParser {
  virtual AbstractCommand *getCommand(const std::string &commandStr) {
    if (commandStr.find("=") != std::string::npos) {
      return new AssignCommand(commandStr);
    }
    return NULL;
  }
};

class PrintCommand : public AbstractCommand {
private:
  std::string variableName;
public: 
  PrintCommand(std::string startCommand_) : AbstractCommand(startCommand_) {  
     const char *start = "print ";
     int index = startCommand.find(start);        
     variableName = getTrimLine(startCommand.substr(index + strlen(start)));
  }

  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) {
    int variableValue = variables->getValue(variableName);      
    outStream << progId << ": " <<  variableValue << "\n";   
    return true;
  }

  virtual int getTime() {
    return InfoAnalizer::getPrintTime();
  } 
};

class PrintParser : public AbstractParser {
  virtual AbstractCommand *getCommand(const std::string &commandStr) {
    if (commandStr.find("print ") != std::string::npos) {
      return new PrintCommand(commandStr);
    } 
    return NULL;
  }
};

class EndCommand : public AbstractCommand {
public: 
  EndCommand(std::string startCommand_) : AbstractCommand(startCommand_) {  }

  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) {
    return true;
  }

  virtual int getTime() {
    return InfoAnalizer::getEndTime();
  } 
};

class EndParser : public AbstractParser {
  virtual AbstractCommand *getCommand(const std::string &commandStr) {
    if (isEndCommand(commandStr)) {
      return new EndCommand(commandStr);
    } 
    return NULL;
  }
public:
  static bool isEndCommand(const std::string &commandStr) {
    return (commandStr == "end");
  }
};

class UnlockCommand : public AbstractCommand {
public: 
  UnlockCommand(std::string startCommand_) : AbstractCommand(startCommand_) {  }

  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) {
    lockId = 0;
    return true;
  }
  
  virtual int getTime() {
    return InfoAnalizer::getUnlockTime();
  } 
};

class UnlockParser : public AbstractParser {
  virtual AbstractCommand *getCommand(const std::string &commandStr) {
    if ("unlock" == commandStr) {
      return new UnlockCommand(commandStr);
    } 
    return NULL;
  }
};


class LockCommand : public AbstractCommand {
public: 
  LockCommand(std::string startCommand_) : AbstractCommand(startCommand_) {  }

  virtual bool execute(IVariableDict *variables, int progId, int &lockId, std::deque<int> &nextBlockCand, std::ostream &outStream) {
    if (lockId != 0) {//if has lock
      std::deque<int>::iterator pos = find(nextBlockCand.begin(), nextBlockCand.end(), progId);
      if (pos == nextBlockCand.end()) {
        nextBlockCand.push_back(progId);      
      }      
      return false;//if cannot make lock, must stop current program
    } 
    lockId = progId;//create lock
    return true;
  }

  virtual int getTime() {
    return InfoAnalizer::getLockTime();
  } 
};

class LockParser : public AbstractParser {
  virtual AbstractCommand *getCommand(const std::string &commandStr) {
    if ("lock" == commandStr) {
      return new LockCommand(commandStr);
    } 
    return NULL;
  }
};


#endif